za Gabriel L. Manor Supabase omogućuje jednostavno dodavanje autentifikacije vašoj aplikaciji uz ugrađenu podršku za e-poštu, OAuth i čarobne veze. Supabase nudi odličan backend s ugrađenim auth i Row Level Security (RLS), upravljanjem Pogotovo one koje se temelje na Daleko je od lakoće. fine-grained permissions relationships between users and data Možda želite ograničiti radnje kao što su uređivanje ili brisanje podataka vlasnicima resursa, spriječiti korisnike da glasuju o vlastitom sadržaju ili izvršiti različite dozvole za različite uloge korisnika. Ovaj tutorial govori o tome kako to učiniti U A Aplikacija za. Supabase authentication and authorization Next.js Mi ćemo početi s za login i upravljanje sjednicama, a zatim dodajte korištenje Primjenjivao se kroz i a . u Supabase Auth authorization rules Kontrola pristupa na temelju odnosa (ReBAC) Supabase Edge Functions local Policy Decision Point (PDP) Na kraju ćete imati aplikaciju za suradnju u stvarnom vremenu koja podržava i javne i zaštićene radnje - i fleksibilan sustav ovlaštenja koji se može razviti kako vaša aplikacija raste. Što gradimo U ovom priručniku izgradit ćemo aplikaciju za glasanje u realnom vremenu pomoću i To pokazuje kako autentifikaciju i autorizaciju u akciji. Supabase Next.js Aplikacija omogućuje korisnicima da kreiraju ankete, glasuju za druge i upravljaju samo vlastitim sadržajem. za login/signup i kako izvršiti Tko može glasovati, uređivati ili brisati. Supabase Auth authorization policies Upotrijebit ćemo glavne značajke Supabase-a - , , , i —combined with a Model za provedbu pravila pristupa po korisniku i po resursima. Auth Postgres RLS Realtime Edge Functions Relationship-Based Access Control (ReBAC) Tehnologija Stack Svijet Svijet Svijet Svijet Svijet Supabase – Backend-as-a-service za baze podataka, autentifikaciju, funkcije u realnom vremenu i Edge Next.js – Frontend framework za izgradnju aplikacije UI i API putova Permit.io – (za ReBAC) definirati i ocijeniti logiku ovlaštenja putem PDP-a Supabase CLI – za upravljanje i uvođenje Edge funkcija na lokalnoj razini i u proizvodnji pretpostavka Sljedeći.js dopuštenje Sljedeći članakCLI Preduvjeti Svijet Svijet Svijet Svijet Svijet Svijet Node.js je instaliran Baza računa Dozvola.io račun Saznajte više o React/Next.js Početak projekta Repo What Can This App Do? Demo aplikacija je platforma za anketiranje u stvarnom vremenu izgrađena uz Next.js i Supabase, gdje korisnici mogu kreirati ankete i glasati za druge. Svijet Svijet Svijet Svijet Svaki korisnik (autentificiran ili ne) može vidjeti popis javnih anketa Samo autentični korisnici mogu kreirati ankete i glasati Korisnik ne može glasati na anketi koju je stvorio Samo kreator ankete može ga urediti ili izbrisati Tutorial Pregled Slijedit ćemo ove opće korake: Svijet Svijet Svijet Svijet Svijet Svijet Uspostavljanje Supabase projekta, sheme, auth i RLS Izgradite osnovne funkcije aplikacije kao što su kreiranje anketa i glasovanje Pravila za autorizaciju modela definiraju uloge i pravila u Permit.io Stvaranje funkcija Supabase Edge za sinhronizaciju korisnika, dodjelu uloga i provjeru dozvola Provedba politika u prednjem dijelu aplikacije pomoću tih funkcija ruba Hajde da počnemo - Setting up Supabase in the Project Uspostavljanje baze podataka u projektu Opcionalno: Klonirajte predložak Starter Već sam stvorio a On je sa svim kodom koji trebate započeti kako bismo se mogli usredotočiti na provedbu Supabase i Permit.io. Početak hramova GitHub You can clone the project by running the following command: git clone <https://github.com/permitio/supabase-fine-grained-authorization> Nakon što ste klonirali projekt, idite u projektni direktorij i instalirajte ovisnosti: cd realtime-polling-app-nextjs-supabase-permitio npm install Creating a new Project in Supabase Kako započeti: Svijet Svijet Svijet Svijet Idite na https://supabase.com i prijavite se ili kreirajte račun. Kliknite na "Novi projekt" i unesite ime projekta, lozinku i regiju. Nakon što ste ga stvorili, idite na Project Settings → API i zabilježite svoj Project URL i Anon Key – potrebni su vam kasnije. Postavljanje autentifikacije i baze podataka u Supabase-u Upotrijebit ćemo ugrađenu e-poštu / lozinku Auth: Svijet Svijet Svijet U bočnoj traci, idite na Authentication → Providers Omogućite pružatelja e-pošte (Opcionalno) Deaktivirajte potvrdu e-pošte za testiranje, ali je držite uključenom za proizvodnju Stvaranje sheme baze podataka Ova aplikacija koristi tri glavne tablice: , i Koristite ga u upravljačkoj ploči Supabase i pokrenite sljedeće: polls options votes SQL Editor -- Create a polls table CREATE TABLE polls ( id UUID DEFAULT uuid_generate_v4() PRIMARY KEY, question TEXT NOT NULL, created_by UUID REFERENCES auth.users(id) NOT NULL, created_at TIMESTAMP WITH TIME ZONE DEFAULT TIMEZONE('utc', NOW()), creator_name TEXT NOT NULL, expires_at TIMESTAMP WITH TIME ZONE NOT NULL, ); -- Create an options table CREATE TABLE options ( id UUID DEFAULT uuid_generate_v4() PRIMARY KEY, poll_id UUID REFERENCES polls(id) ON DELETE CASCADE, text TEXT NOT NULL, ); -- Create a votes table CREATE TABLE votes ( id UUID DEFAULT uuid_generate_v4() PRIMARY KEY, poll_id UUID REFERENCES polls(id) ON DELETE CASCADE, option_id UUID REFERENCES options(id) ON DELETE CASCADE, user_id UUID REFERENCES auth.users(id), created_at TIMESTAMP WITH TIME ZONE DEFAULT TIMEZONE('utc', NOW()), UNIQUE(poll_id, user_id) ); Omogućavanje sigurnosti na razini linije (RLS) Omogućiti za svaku tablicu i definirati politike: RLS -- Polls policies ALTER TABLE polls ENABLE ROW LEVEL SECURITY; CREATE POLICY "Anyone can view polls" ON polls FOR SELECT USING (true); CREATE POLICY "Authenticated users can create polls" ON polls FOR INSERT TO authenticated WITH CHECK (auth.uid() = created_by); -- Options policies ALTER TABLE options ENABLE ROW LEVEL SECURITY; CREATE POLICY "Anyone can view options" ON options FOR SELECT USING (true); CREATE POLICY "Poll creators can add options" ON options FOR INSERT TO authenticated WITH CHECK ( EXISTS ( SELECT 1 FROM polls WHERE id = options.poll_id AND created_by = auth.uid() ) ); -- Votes policies ALTER TABLE votes ENABLE ROW LEVEL SECURITY; CREATE POLICY "Anyone can view votes" ON votes FOR SELECT USING (true); CREATE POLICY "Authenticated users can vote once" ON votes FOR INSERT TO authenticated WITH CHECK ( auth.uid() = user_id AND NOT EXISTS ( SELECT 1 FROM polls WHERE id = votes.poll_id AND created_by = auth.uid() ) ); Za korištenje funkcija u realnom vremenu baze: Svijet Svijet Svijet U bočnoj traci, idite na Urednik stola Za svaku od tri tablice (polls, opcije, glasovi): Kliknite na tri točke → Uredi tablicu Toggle "Omogući u stvarnom vremenu" Sačuvaj promjene Implementacija Supabase e-pošte autentifikacije u aplikaciji U ovoj demo aplikaciji, svatko može pregledati popis anketa dostupnih na aplikaciji, aktivnih i isteklih. Da biste pregledali pojedinosti ankete, upravljali ili glasovali o bilo kojoj anketi, korisnik mora biti prijavljen. Mi ćemo koristiti e-mail i lozinku kao sredstvo autentifikacije za ovaj projekt. : u .env.local NEXT_PUBLIC_SUPABASE_URL=your_supabase_url NEXT_PUBLIC_SUPABASE_ANON_KEY=your_anon_key Ažurirajte svoju komponentu za prijavu kako biste se bavili prijavom i prijavom putem e-pošte / lozinke: import { useState } from "react"; import { createClient } from "@/utils/supabase/component"; const LogInButton = () => { const supabase = createClient(); async function logIn() { const { error } = await supabase.auth.signInWithPassword({ email, password, }); if (error) { setError(error.message); } else { setShowModal(false); } } async function signUp() { const { error } = await supabase.auth.signUp({ email, password, options: { data: { user_name: userName, }, }, }); if (error) { setError(error.message); } else { setShowModal(false); } } const handleSubmit = async (e: React.FormEvent) => { e.preventDefault(); setError(""); if (isLogin) { await logIn(); } else { await signUp(); } }; return ( <> <button onClick={() => setShowModal(true)} className="flex items-center gap-2 p-2 bg-gray-800 text-white rounded-md"> Log In </button> ... </> ); }; export default LogInButton; Ovdje, mi koristimo Supabase način prijavljivanja u korisnika i način prijavljivanja novog korisnika s njihovom e-poštom i lozinkom. također pohranjujemo ime korisnika u polja u korisničkim metapodatcima. signInWithPassword signUp user_name Također možete koristiti kako bi se korisnici prijavili i preusmjerili: supabase.auth.signOut() import { createClient } from "@/utils/supabase/component"; import { useRouter } from "next/router"; const LogOutButton = ({ closeDropdown }: { closeDropdown: () => void }) => { const router = useRouter(); const supabase = createClient(); const handleLogOut = async () => { await supabase.auth.signOut(); closeDropdown(); router.push("/"); }; return ( ... ); }; export default LogOutButton; Ovdje koristimo Metoda iz Supabase za izlazak korisnika i preusmjeriti ih na početnu stranicu. signOut Slušanje za promjene u stanju autentifikacije korisnika Slušanje o promjenama u statusu autentifikacije korisnika omogućuje nam ažuriranje korisničkog interfejsa na temelju statusa autentifikacije korisnika. Svijet Svijet Svijet Show/hide UI elements like login/logout buttons Uvjetno ograničiti pristup zaštićenim stranicama (kao što je glasovanje ili upravljanje anketama) Ensure only authenticated users can perform restricted actions Upotrijebit ćemo slušati te događaje i ažurirati aplikaciju u skladu s tim. supabase.auth.onAuthStateChange() Svijet Svijet In the Layout.tsx file: Track Global Auth State import React, { useEffect, useState } from "react"; import { createClient } from "@/utils/supabase/component"; import { User } from "@supabase/supabase-js"; const Layout = ({ children }: { children: React.ReactNode }) => { const [user, setUser] = useState<User | null>(null); useEffect(() => { const fetchUser = async () => { const supabase = createClient(); const { data } = supabase.auth.onAuthStateChange((event, session) => { setUser(session?.user || null); }); return () => { data.subscription.unsubscribe(); }; }; fetchUser(); }, []); return ( ... ); }; export default Layout; Ograničiti pristup zaštićenim stranicama On pages like ili , također biste trebali slušati promjene stanja autentifikacije kako biste spriječili neovjerene korisnike da im pristupe. poll details poll management Evo kako to izgleda u : u pages/polls/[id].tsx import { createClient } from "@/utils/supabase/component"; import { User } from "@supabase/supabase-js"; const Page = () => { const [user, setUser] = useState<User | null>(null); useEffect(() => { const fetchUser = async () => { const supabase = createClient(); const { data } = supabase.auth.onAuthStateChange((event, session) => { setUser(session?.user || null); setLoading(false); }); return () => { data.subscription.unsubscribe(); }; }; fetchUser(); }, []); return ( ... ); export default Page; And a similar pattern applies in , gdje korisnici trebaju vidjeti vlastite ankete samo ako su prijavljeni: pages/polls/manage.tsx import { createClient } from "@/utils/supabase/component"; import { User } from "@supabase/supabase-js"; const Page = () => { const [user, setUser] = useState<User | null>(null); const supabase = createClient(); useEffect(() => { const fetchUser = async () => { const { data } = supabase.auth.onAuthStateChange((event, session) => { setUser(session?.user || null); if (!session?.user) { setLoading(false); } }); return () => { data.subscription.unsubscribe(); }; }; fetchUser(); }, []); return ( ... ); }; export default Page; Ovi obrasci osiguravaju da vaš UI odražava trenutni status autentifikacije korisnika i predstavlja osnovu za provjere ovlaštenja koje ćemo kasnije dodati. Objekt kada se zove Edge Funkcija za određivanje je li korisniku dopušteno glasovati ili upravljati određenom anketom. user checkPermission Izgradnja funkcionalnosti aplikacije Polling With Supabase configured and authentication working, we can now build the core functionality of the polling app. In this section, we’ll cover: Svijet Svijet Svijet Stvaranje novih anketa Preuzimanje i prikazivanje anketa u realnom vremenu Uvođenje sustava glasovanja This gives us the basic app behavior that we’ll soon protect with fine-grained permissions. Creating New Polls Korisnici moraju biti prijavljeni kako bi mogli kreirati ankete.Svako istraživanje uključuje pitanje, datum isteka i skup opcija.Mi također bilježe tko je stvorio istraživanje kako bismo kasnije mogli koristiti taj odnos za kontrolu pristupa. Unutar , preuzmite autentificiranog korisnika i upotrijebite Supabase za umetanje ankete i njezinih opcija: NewPoll.tsx import React, { useEffect, useState } from "react"; import { createClient } from "@/utils/supabase/component"; import { User } from "@supabase/supabase-js"; const NewPoll = () => { const [user, setUser] = useState<User | null>(null); const supabase = createClient(); useEffect(() => { const fetchUser = async () => { const supabase = createClient(); const { data } = supabase.auth.onAuthStateChange((event, session) => { setUser(session?.user || null); }); return () => { data.subscription.unsubscribe(); }; }; fetchUser(); }, []); const handleSubmit = async (e: React.FormEvent) => { e.preventDefault(); if (question.trim() && options.filter(opt => opt.trim()).length < 2) { setErrorMessage("Please provide a question and at least two options."); return; } // Create the poll const { data: poll, error: pollError } = await supabase .from("polls") .insert({ question, expires_at: new Date(expiryDate).toISOString(), created_by: user?.id, creator_name: user?.user_metadata?.user_name, }) .select() .single(); if (pollError) { console.error("Error creating poll:", pollError); setErrorMessage(pollError.message); return; } // Create the options const { error: optionsError } = await supabase.from("options").insert( options .filter(opt => opt.trim()) .map(text => ({ poll_id: poll.id, text, })) ); if (!optionsError) { setSuccessMessage("Poll created successfully!"); handleCancel(); } else { console.error("Error creating options:", optionsError); setErrorMessage(optionsError.message); } }; return ( ... ); }; export default NewPoll; We’ll later call an Edge Function here to assign the “creator” role in Permit.io. Fetching and Displaying Polls Polls are divided into (Još nije istekao) i Možete ih preuzeti pomoću upita Supabase filtriranih trenutnim vremenskim žigom i postaviti pretplate u realnom vremenu kako biste odmah odražavali promjene. active past Primjer iz : u pages/index.tsx import { PollProps } from "@/helpers"; import { createClient } from "@/utils/supabase/component"; export default function Home() { const supabase = createClient(); useEffect(() => { const fetchPolls = async () => { setLoading(true); const now = new Date().toISOString(); try { // Fetch active polls const { data: activePolls, error: activeError } = await supabase .from("polls") .select( ` id, question, expires_at, creator_name, created_by, votes (count) ` ) .gte("expires_at", now) .order("created_at", { ascending: false }); if (activeError) { console.error("Error fetching active polls:", activeError); return; } // Fetch past polls const { data: expiredPolls, error: pastError } = await supabase .from("polls") .select( ` id, question, expires_at, creator_name, created_by, votes (count) ` ) .lt("expires_at", now) .order("created_at", { ascending: false }); if (pastError) { console.error("Error fetching past polls:", pastError); return; } setCurrentPolls(activePolls); setPastPolls(expiredPolls); } catch (error) { console.error("Unexpected error fetching polls:", error); } finally { setLoading(false); } }; fetchPolls(); // Set up real-time subscription on the polls table: const channel = supabase .channel("polls") .on( "postgres_changes", { event: "*", schema: "public", table: "polls", }, fetchPolls ) .subscribe(); return () => { supabase.removeChannel(channel); }; }, []); return ( ... ); } Pretraživanje i upravljanje korisničkim anketama Ovdje prikupljamo aktivne i prethodne ankete iz Također postavljamo pretplatu u realnom vremenu kako bismo slušali promjene u Da bismo razlikovali između aktivnih i prošlih anketa, uspoređujemo datum isteka svakog anketa s trenutnim datumom. polls polls ažuriranje na stranica za preuzimanje i prikazivanje samo anketa koje je stvorio korisnik: pages/manage.tsx import { PollProps } from "@/helpers"; const Page = () => { useEffect(() => { if (!user?.id) return; const fetchPolls = async () => { try { const { data, error } = await supabase .from("polls") .select( ` id, question, expires_at, creator_name, created_by, votes (count) ` ) .eq("created_by", user.id) .order("created_at", { ascending: false }); if (error) { console.error("Error fetching polls:", error); return; } setPolls(data || []); } catch (error) { console.error("Unexpected error fetching polls:", error); } finally { setLoading(false); } }; fetchPolls(); // Set up real-time subscription const channel = supabase .channel(`polls_${user.id}`) .on( "postgres_changes", { event: "*", schema: "public", table: "polls", filter: `created_by=eq.${user.id}`, }, fetchPolls ) .subscribe(); return () => { supabase.removeChannel(channel); }; }, [user]); return ( ... ); }; export default Page; Here, we only fetch polls created by the user and listen for real-time updates in the tablicu tako da je UI ažuriran s najnovijim podacima o anketama. polls Also, update the komponenta tako da ako je prijavljeni korisnik kreator ankete, ikone za uređivanje i brisanje ankete će im se prikazati na anketi. PollCard import { createClient } from "@/utils/supabase/component"; import { User } from "@supabase/supabase-js"; const PollCard = ({ poll }: { poll: PollProps }) => { const [user, setUser] = useState<User | null>(null); useEffect(() => { const supabase = createClient(); const fetchUser = async () => { const { data } = supabase.auth.onAuthStateChange((event, session) => { setUser(session?.user || null); setLoading(false); }); return () => { data.subscription.unsubscribe(); }; }; fetchUser(); }, []); return ( ... )} </Link> ); }; export default PollCard; Dakle, sada, na kartici ankete, ako je prijavljeni korisnik kreator ankete, prikazat će im se ikone za uređivanje i brisanje ankete. Uvođenje sustava glasovanja Logika glasovanja primjenjuje: Svijet Svijet Svijet Svijet Samo jedan glas po korisniku po anketi Kreatori ne mogu glasati na vlastitim anketama Glasovi se pohranjuju u tablicu za glasovanje Rezultati se prikazuju i ažuriraju u realnom vremenu Let’s break down how this works in the komponentima : ViewPoll.tsx Potreban nam je ID trenutačnog korisnika kako bismo odredili prihvatljivost za glasovanje i zabilježili njihov glas. Fetch the Logged-In User import { createClient } from "@/utils/supabase/component"; import { User } from "@supabase/supabase-js"; const ViewPoll = () => { const [user, setUser] = useState<User | null>(null); const supabase = createClient(); useEffect(() const fetchUser = async () => { const { data: { user }, } = await supabase.auth.getUser(); setUser(user); }; fetchUser(); }, []); Nakon što imamo korisnika, prikupljamo: Load Poll Details and Check Voting Status Svijet Sam izbor (uključujući opcije i brojanje glasova) Da li je ovaj korisnik već glasao Također ćemo ih nazvati kasnije u ažuriranjima u realnom vremenu. useEffect(() => { if (!user) { return; } const checkUserVote = async () => { const { data: votes } = await supabase .from("votes") .select("id") .eq("poll_id", query.id) .eq("user_id", user.id) .single(); setHasVoted(!!votes); setVoteLoading(false); }; const fetchPoll = async () => { const { data } = await supabase .from("polls") .select( ` *, options ( id, text, votes (count) ) ` ) .eq("id", query.id) .single(); setPoll(data); setPollLoading(false); checkUserVote(); }; fetchPoll(); Listen for Real-Time Updates Prihvaćamo promjene u Kada se glasa novo, prikupljamo ažurirane podatke o anketama i status glasa. votes const channel = supabase .channel(`poll-${query.id}`) .on( "postgres_changes", { event: "*", schema: "public", table: "votes", filter: `poll_id=eq.${query.id}`, }, () => { fetchPoll(); checkUserVote(); } ) .subscribe(); return () => { supabase.removeChannel(channel); }; }, [query.id, user]); Handle the Vote Submission Ako korisnik nije glasao i dopušteno je glasovati (dodat ćemo provjeru dopuštenja kasnije), umetnemo njegov glas. const handleVote = async (optionId: string) => { if (!user) return; try { const { error } = await supabase.from("votes").insert({ poll_id: query.id, option_id: optionId, user_id: user.id, }); if (!error) { setHasVoted(true); } } catch (error) { console.error("Error voting:", error); } }; Izračunamo ukupni broj glasova i odbrojavanje do vremena isteka, a zatim ga možete koristiti za prikaz postupka ili statistike. Display the Poll Results if (!poll || pollLoading || voteLoading) return <div>Loading...</div>; // 6. calculate total votes const totalVotes = calculateTotalVotes(poll.options); const countdown = getCountdown(poll.expires_at); return ( ... ); }; export default ViewPoll; With this setup in place, your voting system is fully functional. But right now, anyone logged in could technically try to vote—even on their own poll. Next, we’ll add korištenje and za provođenje tih pravila. authorization checks Permit.io Supabase Edge Functions Prije nego što to učinimo, prvo pogledajmo vrstu sloja ovlaštenja koji ćemo implementirati. ReBAC (Relationship-Based Access Control) – kontrola pristupa na temelju odnosa Supabase handles authentication and basic row-level permissions well, but it doesn’t support complex rules like: Svijet Svijet Kako spriječiti korisnike da glasaju na vlastitim anketama Assigning per-resource roles (like “creator” for a specific poll) Managing access via external policies Da bismo podržali ove vrste dozvola zasnovanih na odnosima, implementirat ćemo ReBAC s Permit.io. Umjesto da se oslanja isključivo na uloge ili atribute (kao u RBAC-u ili ABAC-u), ReBAC određuje pristup procjenjujući kako je korisnik povezan s resursom koji pokušavaju pristupiti. Relationship-Based Access Control (ReBAC) Kontrola pristupa na temelju odnosa (ReBAC) In this tutorial, we apply ReBAC to a polling app: Svijet Svijet Korisnik koji je stvorio anketu je jedini koji može upravljati (uredi / izbriši) Korisnik ne može glasati na vlastitoj anketi Other authenticated users can vote once per poll Modeliranjem tih odnosa u Permit.io-u možemo definirati pravila pristupa koja nadilaze ugrađenu sigurnost na razini linije (RLS) tvrtke Supabase. Za više informacija o ReBAC-u provjerite . u Permit.io’s ReBAC dokazi Dizajn kontrole pristupa For our Polling app, we will define: Svijet Svijet Svijet Jedan resurs s akcijama specifičnim za resurse: ankete: stvaranje, čitanje, brisanje, ažuriranje. Two roles for granting permission levels based on a user’s relationship with the resources: Can perform and actions in polls. Can not , or actions in polls. authenticated: create read delete update Can , , , and actions in polls. Can perform and actions in votes. Cannot use on their own polls. creator: create read delete update read create create Preuzimanje dozvola.io Let’s walk through setting up the authorization model in Permit. Svijet Svijet Stvoriti novi projekt u Permit.io Nazovite ga nešto poput supabase-polling Definicija resursa za ankete Idite na karticu Politika → resursi Kliknite na "Stvoriti resurs" Nazovite to ankete i dodajte akcije: čitati, stvoriti, ažurirati, izbrisati Omogućiti ReBAC za resurs U odjeljku "ReBAC opcije" definirajte sljedeće uloge: autentificirani kreator Kliknite Save Create a new project Preuzmite uloge koje smo upravo stvorili.Upozorite da je dopuštenje stvorilo podrazumijevane uloge ( , , To je nepotrebno za ovaj tutorial. admin editor user Svijet Svijet Svijet Define access policies Go to the tab Policy → Policies Use the visual matrix to define: can and polls authenticated read create can , , and polls creator read update delete (Optional) You can configure vote permissions as part of this or via a second resource if you model votes separately Dodaj instance resursa Idi u Direktorij → Instance Dodaj pojedinačne ID-ove anketa kao instance resursa (to ćete automatizirati kasnije kada se kreiraju nove ankete) Dodijelite uloge korisnicima po anketi (npr. user123 je kreator ankete456) This structure gives us the power to write flexible access rules and enforce them per user, per poll. Now that we have completed the initial setup on the Permit dashboard, let's use it in our application. Next, we’ll connect na naš projekt Supabase putem Edge Functions koji: dopuštenje Svijet Svijet Svijet Svijet Sinkronizirati nove korisnike Dodjeljivanje uloga kreatora Provjerite pristup na zahtjev Setting up Permit in the Polling Application Dozvola nudi nekoliko načina za integraciju s vašom aplikacijom, ali mi ćemo koristiti Container PDP za ovaj tutorial. Morate gostovati kontejner online da biste ga pristupili u funkcijama Supabase Edge. . Once you have hosted it, save the url for your container. Željeznica.com Pronađite ključ API-ja za dopuštenje klikom na "Projekti" u bočnoj traci nadzorne ploče za dopuštenje, navigacijom prema projektu na kojem radite, klikom na tri točke i odabirom "Kopiraj ključ API-ja". Creating Supabase Edge Function APIs for Authorization Oni su savršeni za integraciju usluga trećih strana kao što je Permit.io. Mi ćemo ih koristiti za provedbu naših pravila ReBAC-a u tijeku provjeravanjem je li korisnicima dopušteno obavljati određene radnje na anketama. Supabase Edge Functions Create Functions in Supabase Inicijalizirajte Supabase u projektu i kreirajte tri različite funkcije pomoću Ovo će biti početna točka za vaše funkcije: supabase functions new npx supabase init npx supabase functions new syncUser npx supabase functions new updateCreatorRole npx supabase functions new checkPermission To će stvoriti a Brošura u Sada, hajde da napišemo kode za svaku krajnju točku. functions supabase Sljedeći Članak Sljedeći Članak Sinhroniziranje korisnika na Permit.io na Signup ( ) i syncUser.ts Ova funkcija sluša za Supabase's auth event. When a new user signs up, we sync their identity to Permit.io and assign them the default Uloga je. SIGNED_UP authenticated import "jsr:@supabase/functions-js/edge-runtime.d.ts"; import { Permit } from "npm:permitio"; const corsHeaders = { 'Access-Control-Allow-Origin': "*", 'Access-Control-Allow-Headers': 'Authorization, x-client-info, apikey, Content-Type', 'Access-Control-Allow-Methods': 'POST, GET, OPTIONS, PUT, DELETE', } // Supabase Edge Function to sync new users with Permit.io Deno.serve(async (req) => { const permit = new Permit({ token: Deno.env.get("PERMIT_API_KEY"), pdp: "<https://real-time-polling-app-production.up.railway.app>", }); try { const { event, user } = await req.json(); // Only proceed if the event type is "SIGNED_UP" if (event === "SIGNED_UP" && user) { const newUser = { key: user.id, email: user.email, name: user.user_metadata?.name || "Someone", }; // Sync the user to Permit.io await permit.api.createUser(newUser); await permit.api.assignRole({ role: "authenticated", tenant: "default", user: user.id, }); console.log(`User ${user.email} synced to Permit.io successfully.`); } // Return success response return new Response( JSON.stringify({ message: "User synced successfully!" }), { status: 200, headers: corsHeaders }, ); } catch (error) { console.error("Error syncing user to Permit: ", error); return new Response( JSON.stringify({ message: "Error syncing user to Permit.", "error": error }), { status: 500, headers: { "Content-Type": "application/json" } }, ); } }); Prikazivanje uloge Stvoritelja ( ) i updateCreatorRole.ts Nakon što korisnik stvori anketu, ova se funkcija poziva na: Svijet Svijet Sinhronizirajte anketu kao novu instanci resursa Permit.io Assign the user the role for that poll creator import "jsr:@supabase/functions-js/edge-runtime.d.ts"; import { Permit } from "npm:permitio"; const corsHeaders = { 'Access-Control-Allow-Origin': "*", 'Access-Control-Allow-Headers': 'Authorization, x-client-info, apikey, Content-Type', 'Access-Control-Allow-Methods': 'POST, GET, OPTIONS, PUT, DELETE', } Deno.serve(async (req) => { const permit = new Permit({ token: Deno.env.get("PERMIT_API_KEY"), pdp: "<https://real-time-polling-app-production.up.railway.app>", }); try { const { userId, pollId } = await req.json(); // Validate input parameters if (!userId || !pollId) { return new Response( JSON.stringify({ error: "Missing required parameters." }), { status: 400, headers: { "Content-Type": "application/json" } }, ); } // Sync the resource (poll) to Permit.io await permit.api.syncResource({ type: "polls", key: pollId, tenant: "default", attributes: { createdBy: userId } }); // Assign the creator role to the user for this specific poll await permit.api.assignRole({ role: "creator", tenant: "default", user: userId, resource: { type: "polls", key: pollId, } }); return new Response( JSON.stringify({ message: "Creator role assigned successfully", success: true }), { status: 200, headers: corsHeaders }, ); } catch (error) { console.error("Error assigning creator role: ", error); return new Response( JSON.stringify({ message: "Error occurred while assigning creator role.", error: error }), { status: 500, headers: { "Content-Type": "application/json" } }, ); } }); Sljedeći Članak Pregled dopuštenja ( ) i checkPermission.ts Ova funkcija djeluje kao čuvar vrata – provjerava je li korisniku dopušteno izvršiti određenu akciju ( , , , Na posebnoj anketi. create read update delete import "jsr:@supabase/functions-js/edge-runtime.d.ts"; import { Permit } from "npm:permitio"; const corsHeaders = { "Access-Control-Allow-Origin": "*", "Access-Control-Allow-Headers": "Authorization, x-client-info, apikey, Content-Type", "Access-Control-Allow-Methods": "POST, GET, OPTIONS, PUT, DELETE", }; Deno.serve(async req => { const permit = new Permit({ token: Deno.env.get("PERMIT_API_KEY"), pdp: "<https://real-time-polling-app-production.up.railway.app>", }); try { const { userId, operation, key } = await req.json(); // Validate input parameters if (!userId || !operation || !key) { return new Response( JSON.stringify({ error: "Missing required parameters." }), { status: 400, headers: { "Content-Type": "application/json" } } ); } // Check permissions using Permit's ReBAC const permitted = await permit.check(userId, operation, { type: "polls", key, tenant: "default", // Include any additional attributes that Permit needs for relationship checking attributes: { createdBy: userId, // This will be used in Permit's policy rules }, }); return new Response(JSON.stringify({ permitted }), { status: 200, headers: corsHeaders, }); } catch (error) { console.error("Error checking user permission: ", error); return new Response( JSON.stringify({ message: "Error occurred while checking user permission.", error: error, }), { status: 500, headers: { "Content-Type": "application/json" } } ); } }); Local Testing Počnite svoj Supabase dev server kako biste testirali funkcije lokalno: npx supabase start npx supabase functions serve Tada možete dodirnuti svoje funkcije na: <http://localhost:54321/functions/v1/><function-name> Primjer : <http://localhost:54321/functions/v1/checkPermission> Integracija provjera ovlaštenja u UI Now that we’ve created our authorization logic with Permit.io and exposed it via Supabase Edge Functions, it’s time to enforce those checks inside the app’s components. U ovom odjeljku ažurirat ćemo ključne komponente korisničkog interfejsa kako bismo pozvali te funkcije i uvjetno omogućili ili blokirali korisničke akcije kao što su glasovanje ili upravljanje anketama na temelju provjera dozvola. Dodijeliti ulogu kreatora nakon kreiranja ankete NewPoll.tsx Sljedeći članakPoll.tsx Nakon što kreiramo anketu i sačuvamo je u Supabase, zovemo Funkcija je: updateCreatorRole Svijet Svijet Svijet Sinhronizirajte novu anketu kao resurs u Permit.io Korisnik koji je stvorio funkciju za postavljanje postavke za trenutni korisnik je stvorio funkciju za postavljanje postavke za trenutni korisnik (Error.Trim() &&options.filter(opt => opt.trim()).Length < 2) { setErrorMessage("ExpiryDate).to ISOString("Please provide a question and at least two options."); return; try { // Create the error const: user_metadata?.Anuser_name, await error: pollError() &&options.filter(opt => opt.trim()) .insert{ question, expires_at: Date new(ExpiryDate).to ISOString), created_by user_error_role(Error_name, error_response: user_metadata?.Anuser_ Ograničenje glasovanja na temelju dozvola ViewPoll.tsx Prije nego što dopustimo korisniku da glasa na anketi, zovemo funkcije kako bi se provjerilo da imaju Dopuštenje za Evo kako se primjenjuje pravilo: checkPermission create votes “A creator cannot vote on their own poll.” Check voting permission: const [canVote, setCanVote] = useState(false); useEffect(() => { const checkPermission = async () => { if (!user || !query.id) return; try { const response = await fetch("<http://127.0.0.1:54321/functions/v1/checkPermission>", { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify({ userId: user.id, operation: "create", key: query.id, }), }); const { permitted } = await response.json(); setCanVote(permitted); } catch (error) { console.error("Error checking permission:", error); setCanVote(false); } }; checkPermission(); }, [user, query.id]); Disable vote buttons if user isn’t allowed: <button onClick={() => handleVote(option.id)} disabled={!user || !canVote}} className="w-full text-left p-4 rounded-md hover:bg-slate-100 transition-colors disabled:opacity-50 disabled:cursor-not-allowed"> {option.text} </button> Show a message if the user is not allowed to vote: {user && !canVote && ( <p className="mt-4 text-gray-600">You cannot vote on your own poll</p> )} : Control Access to Edit/Delete PollCard.tsx Također ograničavamo radnje upravljanja anketama (uređivanje i brisanje) provjeravanjem ima li korisnik ili Odobrenje za tu anketu. update delete Check management permissions: const [canManagePoll, setCanManagePoll] = useState(false); useEffect(() => { const checkPollPermissions = async () => { if (!user || !poll.id) return; try { // Check for both edit and delete permissions const [editResponse, deleteResponse] = await Promise.all([ fetch("<http://127.0.0.1:54321/functions/v1/checkPermission>", { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify({ userId: user.id, operation: "update", key: poll.id, }), }), fetch("/api/checkPermission", { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify({ userId: user.id, operation: "delete", key: poll.id, }), }), ]); const [{ permitted: canEdit }, { permitted: canDelete }] = await Promise.all([editResponse.json(), deleteResponse.json()]); // User can manage poll if they have either edit or delete permission setCanManagePoll(canEdit || canDelete); } catch (error) { console.error("Error checking permissions:", error); setCanManagePoll(false); } }; checkPollPermissions(); }, [user, poll.id]); Conditionally show management buttons: Zamjena za: {user?.id === poll?.created_by && ( sa : {canManagePoll && ( <div className="flex justify-start gap-4 mt-4"> <button type="button" onClick={handleEdit}> </button> <button type="button" onClick={handleDelete}> </button> </div> )} Testiranje integracije Nakon integracije, trebali biste vidjeti sljedeće ponašanje u aplikaciji: Svijet Svijet Svijet Svijet Svijet Isključeni korisnici mogu vidjeti ankete, ali ne i komunicirati Autentificirani korisnici mogu glasati na anketama koje nisu stvorili Kreatori ne mogu glasati na vlastitim anketama Samo kreatori vide opcije za uređivanje / brisanje na svojim anketama You should be able to see the application's changes by going to the browser. On the home screen, users can view the list of active and past polls, whether they are logged in or not. However, when they click on a poll, they will not be able to view the poll details or vote on it. Instead, they will be prompted to log in. Once logged in, the user can view the details of the poll and vote on it. However, if the user is the creator of the poll, they will not be able to vote on it. They will see a message indicating that they cannot vote on their own poll. They will also be allowed to manage any poll that they create. Zaključak In this tutorial, we explored how to implement U stvarnom svijetu Aplikacija za. Supabase authentication and authorization Next.js Počeli smo s postavljanjem za prijavu i prijavu, stvorio relacijsku shemu s sigurnosnim sustavom razine rublja i dodao dinamičku logiku ovlaštenja pomoću Uz pomoć i a , izvršili smo provjere dozvola izravno s frontenda. Supabase Auth ReBAC Supabase Edge Functions Policy Decision Point (PDP) Kombinirajući S fleksibilnom kontrolom pristupa mogli smo: Supabase Auth Svijet Svijet Svijet Svijet Svijet Autentificiranje korisnika putem e-pošte i lozinke Ograničiti glasovanje i upravljanje anketama na ovlaštene korisnike Sljedeći Članak Sprječavanje kreatora da glasaju na vlastitim anketama Dodjeljivanje i ocjenjivanje uloga korisnika na temelju odnosa s podacima Ova postavka pruža vam skalabilnu osnovu za izgradnju aplikacija koje zahtijevaju autentifikaciju i autorizaciju s finim zrnom. daljnje čitanje Svijet Svijet Svijet Svijet Svijet Svijet Permit.io ReBAC vodič Dozvola + pružatelji autentikacije Elementi dopuštenja: ugrađeni UI za upravljanje ulogama Filtriranje podataka s dopuštenjem Revizorski logovi Imate pitanja? pridružite nam se , gdje stotine programera grade i raspravljaju o autorizaciji. Slack zajednica Slack zajednica